home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.Draw / Window.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  47.8 KB  |  1,598 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* This file contains the code for the document procedure pointers for the main DTS.Draw
  20. ** document.  DTS.Draw currently only supports one type of file-based documents,
  21. ** type 'DDOC'. */
  22.  
  23. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  24.  
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29.  
  30.  
  31. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  32. #include "App.defs.h"        /* Get various application definitions.            */
  33. #include "App.protos.h"        /* Get the prototypes for application.            */
  34.  
  35. #ifndef __ERRORS__
  36. #include <Errors.h>
  37. #endif
  38.  
  39. #ifndef __FONTS__
  40. #include <Fonts.h>
  41. #endif
  42.  
  43. #ifndef __RESOURCES__
  44. #include <Resources.h>
  45. #endif
  46.  
  47. #ifndef __TOOLUTILS__
  48. #include <ToolUtils.h>
  49. #endif
  50.  
  51. #ifndef __TREEOBJ2__
  52. #include "TreeObj2.h"
  53. #endif
  54.  
  55. #ifndef __UTILITIES__
  56. #include "Utilities.h"
  57. #endif
  58.  
  59.  
  60.  
  61. /*****************************************************************************/
  62.  
  63.  
  64.  
  65. typedef struct {
  66.     TreeObjHndl    root;
  67.     short        cnum;
  68. } LayerDrawInfo;
  69.  
  70. Boolean            gNoDefaultDocument = false;
  71.                     /* Set to true if app should boot with no default document. */
  72.                     /* This tells DTS.Lib..framework what you want. */
  73.  
  74. OSType            gAppWindowType = kDocFileType;    /* Main document type. */
  75. long            gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  76.  
  77. short            gMinVersion = kMinVersion;    /* Minimum document version app can support. */
  78. short            gMaxVersion = kMaxVersion;    /* Maximum document version app can support. */
  79.                                             /* More informing DTS.Lib..framework. */
  80.  
  81. extern short    gPrintPage;                    /* Non-zero means we are printing. */
  82.                                             /* DTS.Lib..framework global. */
  83.  
  84. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  85. extern CursPtr        gCursorPtr;                /* to know about these things. */
  86.                                             /* Above are DTS.Lib..framework globals. */
  87.  
  88. static void        AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click);
  89. static void        SlideSelection(FileRecHndl frHndl, ClickInfo *click);
  90. static OSErr    WindowLayerProc(LayerObj theLayer, short message);
  91. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  92. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  93. static void        DrawPageGrid(void);
  94.  
  95. #define kDataAreaPadding 0
  96.  
  97.  
  98. /*****************************************************************************/
  99. /*****************************************************************************/
  100.  
  101.  
  102.  
  103. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  104.  
  105. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  106. ** You are passed an empty region.  You are supposed to add any custom frame
  107. ** parts that this document uses.  Typically there are no frame portions, as
  108. ** they are accounted for in other ways.  The scrollbars and grow icon will
  109. ** automatically be contributed to the calculation of the frame region.
  110. ** If you use sidebars, these are also added in automatically.  This is only
  111. ** used if the frame region is more complicated than can automatically be
  112. ** handled.  So, almost always, you will simply leave the region empty. */
  113.  
  114. #pragma segment TheDoc
  115. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  116. {
  117. #pragma unused (frHndl, window, rgn)
  118. }
  119.  
  120.  
  121.  
  122. /*****************************************************************************/
  123.  
  124.  
  125.  
  126. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  127.  
  128. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  129. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  130. ** further process the click. */
  131.  
  132. #pragma segment TheDoc
  133. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  134. {
  135. #pragma unused (firstClick)
  136.  
  137.     FileRecHndl        frHndl;
  138.     ControlHandle    ctl;
  139.     short            dblClick, shiftMod;
  140.     Point            contOrg;
  141.     Boolean            selected;
  142.     TreeObjHndl        hitObj, root;
  143.     ClickInfo        click;
  144.  
  145.     SetPort(window);
  146.  
  147.     if (IsCtlEvent(window, event, &ctl, &dblClick)) return;
  148.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  149.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  150.         ** controls in the content besides the document scrollbars. */
  151.  
  152.     frHndl = (FileRecHndl)GetWRefCon(window);
  153.     if ((*frHndl)->fileState.readOnly) return;
  154.         /* Don't allow changes if read-only. */
  155.  
  156.     /* If none of the above resolved why we are here, then the click was in the
  157.     ** actual drawing area of the content, and we have some serious work to do. */
  158.  
  159.     GetContentOrigin(window, &contOrg);
  160.     SetOrigin(contOrg.h, contOrg.v);
  161.         /* This sets the origin of the document area of the content to the position
  162.         ** indicated by the document scrollbars. */
  163.  
  164.     click.localEvent = *event;
  165.     GlobalToLocal(&click.localEvent.where);
  166.         /* Who wants to work in global coordinates, anyway... */
  167.  
  168.     if (!GetTool()) {            /* If no specific tool (arrow tool)... */
  169.  
  170.         click.message = HITTESTGRABBER;
  171.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  172.             /* See if the user clicked on a grabber for a selected object. */
  173.  
  174.         if (!hitObj) {        /* If user didn't click on a grabber, see if they hit an object. */
  175.             click.message = HITTESTOBJ;
  176.             hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  177.         }
  178.  
  179.         shiftMod = click.localEvent.modifiers & shiftKey;
  180.             /* Find out if the user was holding down the shift key. */
  181.  
  182.         if (click.message == HITTESTGRABBER)
  183.             shiftMod = 0;
  184.                 /* Pretent that the shift key isn't held down if they are on a grabber.  If
  185.                 ** they are on a grabber, you want to do a constrain with the shift key. */
  186.  
  187.         if (hitObj) {        /* User hit either a grabber or the object itself. */
  188.  
  189.             selected = mDerefCommon(hitObj)->selected;
  190.                 /* See if the object hit, independent of type, was selected. */
  191.  
  192.             if ((!selected) && (!shiftMod))        /* The object isn't selected, and */
  193.                 DoTreeSelect(root, SELECTOFF);    /* the shift key isn't held down, */
  194.                                                 /* so deselect all other objects. */
  195.  
  196.             click.message = CLICKSELECT;
  197.             if ((!selected) || (shiftMod))
  198.                 DoTreeObjMethodClipped(hitObj, CLICKMESSAGE, (long)&click);
  199.                     /* If the object isn't selected, or if the shift key is down, change
  200.                     ** the select state of the object that was clicked on. */
  201.  
  202.             click.localEvent.modifiers ^= shiftMod;
  203.                 /* Turn off the shift modifier, which makes the rest of the
  204.                 ** handling of the object easier. */
  205.  
  206.             if (mDerefCommon(hitObj)->selected) {    /* If object selected, do something with it. */
  207.                 if (click.grabber == -1)
  208.                     SlideSelection(frHndl, &click);
  209.                 else
  210.                     AddOrSizeObj(frHndl, hitObj, &click);
  211.             }            /* The click could have been used to shift-click deselect an object,
  212.                         ** so only do something with the object if it is selected.  We  either
  213.                         ** resize the object (if a grabber was clicked on) or slide the selected
  214.                         ** objects (if an object was clicked on). */
  215.  
  216.         }
  217.         else {                                    /* No object was clicked on... */
  218.  
  219.             if (!shiftMod)                            /* If not a shift-click, */
  220.                 DoTreeSelect(root, SELECTOFF);        /* deselect everything.  */
  221.  
  222.             AddOrSizeObj(frHndl, hitObj, &click);    /* Do extend select. */
  223.         }
  224.     }
  225.     else {
  226.         click.message = HITTESTGRABBER;
  227.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  228.             /* See if the user clicked on a grabber for a selected object. */
  229.         AddOrSizeObj(frHndl, hitObj, &click);
  230.             /* No object clicked on, so add an object. */
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. /*****************************************************************************/
  237.  
  238.  
  239.  
  240. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  241.  
  242. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  243. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  244. **
  245. ** 1) If you handle the key, return(true).  This completes the key handling.
  246. ** 2) If you don't handle the key, you return false.  However, there are two
  247. **    situations for not handling the key:
  248. **      a) You want someone else to.
  249. **      b) You want nobody else to look at the key.
  250. **    This is what the boolean passThrough is for.  If you wish the next window
  251. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  252. **    is already initialized to false, which is the common case, so you only have
  253. **    to worry about setting it true.
  254. **
  255. ** If you have a window that never processes keys and always passes them through,
  256. ** just set the contentKeyProc to nil.  This will indicate to the application
  257. ** framework that all keys should be passed through this window.  DTS.Draw has
  258. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  259. ** to document windows. */
  260.  
  261. #pragma segment TheDoc
  262. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  263. {
  264. #pragma unused (passThrough)
  265.  
  266.     char        key;
  267.     FileRecHndl    frHndl;
  268.  
  269.     key = event->message & charCodeMask;
  270.     if (key != 8) return(false);
  271.         /* The only key we handle is the delete key.  leave for all the rest. */
  272.  
  273.     SetPort(window);
  274.  
  275.     frHndl = (FileRecHndl)GetWRefCon(window);
  276.     if ((*frHndl)->fileState.readOnly) return(false);
  277.         /* Don't allow changes if read-only. */
  278.  
  279.     DoDelete(frHndl);
  280.  
  281.     return(true);
  282. }
  283.  
  284.  
  285.  
  286. /*****************************************************************************/
  287.  
  288.  
  289.  
  290. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  291.  
  292. /* Draw application specific content (Called by DoDrawFrame).
  293. **
  294. ** If your application has any custom frame areas, or if it uses sidebars,
  295. ** this is the function that you would put the frame drawing code.  The
  296. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  297. ** Just do the sidebar and custom areas here. */
  298.  
  299. #pragma segment TheDoc
  300. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  301. {
  302.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  303.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  304.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  305.  
  306.     BeginFrame(window);
  307.     DoDrawControls(window, activate);
  308.     EndFrame(window);
  309. }
  310.  
  311.  
  312.  
  313. /*****************************************************************************/
  314.  
  315.  
  316.  
  317. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  318.  
  319. /* Frees up any application-specific memory in the document.  This is called by
  320. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  321. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  322. **
  323. ** The document may have a bunch of handles off the main handle of the document.
  324. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  325. ** the ram for the main handle of the document, so release everything else
  326. ** here, or you will have a memory leak.
  327. **
  328. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  329. ** hierarchical document (see TreeObj package). */
  330.  
  331. #pragma segment TheDoc
  332. OSErr    FreeDocument(FileRecHndl frHndl)
  333. {
  334.     return(DefaultFreeDocument(frHndl));
  335. }
  336.  
  337.  
  338.  
  339. /*****************************************************************************/
  340.  
  341.  
  342.  
  343. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  344.  
  345. /* Any additional window disposal tasks can be handled here. */
  346.  
  347. #pragma segment TheDoc
  348. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  349. {
  350. #pragma unused (frHndl, window)
  351.  
  352.     return(noErr);
  353. }
  354.  
  355.  
  356.  
  357. /*****************************************************************************/
  358.  
  359.  
  360.  
  361. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  362.  
  363. /* Image the document into the current port.
  364. **
  365. ** The only thing tricky about this function is that it needs to key off of
  366. ** the global variable gPrintPage.  gPrintPage is the current page that is
  367. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  368. **
  369. ** For when printing:
  370. **
  371. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  372. ** the page there are no more pages, you should set gPrintPage to 0.  This
  373. ** indicates to the print loop that the end of the document has been reached.
  374. ** Even if the user indicated in the job dialog to print more pages, setting
  375. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  376. ** because the print loop can't know when printing is done.  The imaging procedure
  377. ** is the logical one to state when everything has been imaged. */
  378.  
  379. #pragma segment TheDoc
  380. OSErr    ImageDocument(FileRecHndl frHndl)
  381. {
  382.     LayerDrawInfo    drawInfo;
  383.     LayerObj        wlyr, blyr;
  384.  
  385.     drawInfo.root = (*frHndl)->d.doc.root;
  386.         /* If there isn't a background layer, then all objects are drawn into
  387.         ** the work layer, and therefore we don't need to fill in the cnum field
  388.         ** of the drawInfo.  The WorkLayerProc will notice that there isn't a
  389.         ** layer behind it and will automatically do the right thing. */
  390.  
  391.     if (gPrintPage) {
  392.         DoTreeDraw(drawInfo.root, DRAWOBJ);        /* Draw the page. */
  393.         gPrintPage = 0;                            /* We only support one page in this sample. */
  394.         return(noErr);
  395.     }
  396.  
  397.     NewLayer(&wlyr, nil, WindowLayerProc, (*frHndl)->fileState.window, 0, (long)&drawInfo);
  398.         /* Create a layer object for the window. */
  399.  
  400.     NewLayer(&blyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  401.         /* Create a background layer for drawing of all of the objects. */
  402.  
  403.     InvalLayer(wlyr, GetEffectiveDstRect(wlyr), false);
  404.         /* We want to draw the entire contents. */
  405.  
  406.     UpdateLayer(wlyr);
  407.         /* Update what's invalid, which is everything.  All the objects are drawn into
  408.         ** the background layer, and then the background layer is transferred into the
  409.         ** window. */
  410.  
  411.     DisposeThisAndBelowLayers(wlyr);
  412.         /* Clean up what we created. */
  413.  
  414.     return(noErr);
  415. }
  416.  
  417.  
  418.  
  419. /*****************************************************************************/
  420.  
  421.  
  422.  
  423. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  424.  
  425. /* This function does the remaining window initialization.
  426. **
  427. ** There may be additional content initialization for the window.  At this point,
  428. ** you have a window, but it is currently invisible.  If you return noErr, then
  429. ** the window will be set to the state indicated for that window.  Why this function?
  430. ** You may wish to add controls to the content of the window.  You may have a
  431. ** TextEdit record in the content.  All of these sort of things can't be created
  432. ** until there is a window to contain them.  First a document is read in, and then
  433. ** if the document creation succeeds, a window is created for that document.
  434. ** At this point we have a document, and we are on our way to having a window.
  435. ** All that remains is any additional content initialization.  Do it, return
  436. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  437. ** and the incomplete window will be disposed of. */
  438.  
  439. #pragma segment TheDoc
  440. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  441. {
  442.     OSErr        err;
  443.     Rect        rct;
  444.     TreeObjHndl    root;
  445.  
  446.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  447.  
  448.     rct = GetDataArea(root = (*frHndl)->d.doc.root);
  449.     SetDataArea(root, rct.right, rct.bottom);
  450.  
  451.     return(err);
  452. }
  453.  
  454.  
  455.  
  456. /*****************************************************************************/
  457.  
  458.  
  459.  
  460. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  461.  
  462. /* The code below assumes that you are using the hierarchical document package.
  463. ** If you are, the entire hierarchical document is read in with just these two
  464. ** calls.  If you don't use it, you are on your own.  See DTS.Chat for an
  465. ** example of an application that uses the DTS.framework without the hierarchical
  466. ** document package. */
  467.  
  468. #pragma segment TheDoc
  469. OSErr    ReadDocument(FileRecHndl frHndl)
  470. {
  471.     OSErr        err;
  472.     TreeObjHndl    root;
  473.  
  474.     err = DefaultReadDocument(frHndl);
  475.     root = (*frHndl)->d.doc.root;
  476.     mDerefRoot(root)->numSelected = 0;        /* User may have saved when objects were selected. */
  477.     if (!err)
  478.         DefaultReadDocumentFixup(frHndl);
  479.  
  480.     return(err);
  481. }
  482.  
  483.  
  484.  
  485. /*****************************************************************************/
  486.  
  487.  
  488.  
  489. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  490.  
  491. /* Resize application specific content (Called by ResizeWindow).
  492. **
  493. ** This gets called when a user does a zoom or window resizing operation.
  494. ** It is possible that things in the content need to be resized in conjunction
  495. ** with the resizing of the window. */
  496.  
  497. #pragma segment TheDoc
  498. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  499. {
  500. #pragma unused (window, oldh, oldv)
  501.  
  502.     /* See DTS.Chat for a sample usage of this function. */
  503. }
  504.  
  505.  
  506.  
  507. /*****************************************************************************/
  508.  
  509.  
  510.  
  511. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  512.  
  513. /* Scroll application specific frame (Called by DoScrollFrame).
  514. **
  515. ** Some applications may need to scroll the "frame" of the document along
  516. ** with the document contents.  This is common for applications with rulers,
  517. ** or other similar sidebar items. */
  518.  
  519. #pragma segment TheDoc
  520. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  521. {
  522. #pragma unused (frHndl, window, dh, dv)
  523. }
  524.  
  525.  
  526.  
  527. /*****************************************************************************/
  528.  
  529.  
  530.  
  531. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  532.  
  533. /* This gets called just prior to and just after an undo/redo operation is done. */
  534.  
  535. #pragma segment TheDoc
  536. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  537. {
  538.     WindowPtr    window;
  539.     TreeObjHndl    root;
  540.     Rect        rct;
  541.  
  542.     window = (*frHndl)->fileState.window;
  543.     root   = (*frHndl)->d.doc.root;
  544.  
  545.     if (!afterUndo)                            /* Before an undo operation we deselect everything. */
  546.         DoTreeSelect(root, SELECTOFF);        /* Only what was undone should end up selected. */
  547.  
  548.     if (afterUndo) {
  549.         rct = GetDataArea(root);
  550.         SetDataArea(root, rct.right, rct.bottom);
  551.         SetContentOrigin(window, contOrg.h, contOrg.v);
  552.             /* Undo may have a different document origin than where the user was.
  553.             ** Scroll the document back to where the edit we are undoing took place. */
  554.  
  555.         BeginContent(window);            /* Redraw the document to display */
  556.         DoImageDocument(frHndl);        /* the changes due to undo. */
  557.         EndContent(window);
  558.     }
  559.  
  560.     DoSetCursor(nil);
  561. }
  562.  
  563.  
  564.  
  565. /*****************************************************************************/
  566.  
  567.  
  568.  
  569. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  570.  
  571. /* This function is where you adjust the cursor to reflect the location in the
  572. ** document or window.  You have the additional input of gCursorRgn to deal
  573. ** with.  The way that the cursor handling works is as follows:
  574. ** 1) The application calls DoWindowCursor().
  575. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  576. **    It looks at the document's windowCursorProc and checks to see if the document
  577. **    has one.  If the document doesn't have one, then it assumes that that window
  578. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  579. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  580. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  581. **    is set to an arrow (for the non-document area of the screen).
  582. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  583. **    job is as follows:
  584. **    a) If the cursor is over a position that is determined by the window, then
  585. **       the proc removes other areas from gCursorRgn.  Note that it should not
  586. **       simply set the area to what it "thinks" is the correct area.  This window
  587. **       may not be the front-most.  Other windows will have already been subtracted
  588. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  589. **       and should be passed to WaitNextEvent calls in the application (already the case
  590. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  591. **       You should also return true, as the cursor has been determined.
  592. **    b) If the cursor is not over a position for this window, then you should
  593. **       return.  You will either pass back true or false.  If you don't wish
  594. **       windows behind this window to have a shot at cursor determination, then
  595. **       return true.  This states that the cursor is "determined".  It is, in the
  596. **       sense that no further determination will occur.  If you return false, then
  597. **       other windows get a shot at determining the cursor.
  598. **
  599. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  600. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  601. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  602. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  603. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  604. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  605. ** to the return result of DoSetResCursor(), and you will be set. */
  606.  
  607. #pragma segment TheDoc
  608. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  609. {
  610. #pragma unused (frHndl)
  611.  
  612.     RgnHandle    frameRgn, contRgn;
  613.  
  614.     if (GetTool()) {
  615.         frameRgn = DoCalcFrameRgn(window);
  616.         contRgn  = NewRgn();
  617.         DiffRgn(((WindowPeek)window)->contRgn, frameRgn, contRgn);
  618.         DisposeRgn(frameRgn);
  619.         if (PtInRgn(globalPt, contRgn)) {
  620.             gCursorPtr = DoSetResCursor(addObjCursor);
  621.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  622.             DisposeRgn(contRgn);
  623.             return(true);
  624.         }
  625.         DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  626.         SectRgn(gCursorRgn, contRgn, gCursorRgn);
  627.         DisposeRgn(contRgn);
  628.     }
  629.  
  630.     SetCursor(gCursorPtr = &qd.arrow);
  631.     return(true);
  632. }
  633.  
  634.  
  635.  
  636. /*****************************************************************************/
  637.  
  638.  
  639.  
  640. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  641.  
  642. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  643. ** to give the application a chance to do any additional tasks related to
  644. ** a window closing.  DTS.Chat doesn't have anything else extra to do. */
  645.  
  646. #pragma segment TheDoc
  647. void    WindowGoneFixup(WindowPtr window)
  648. {
  649. #pragma unused (window)
  650. }
  651.  
  652.  
  653.  
  654. /*****************************************************************************/
  655.  
  656.  
  657.  
  658. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  659.  
  660. /* The reverse function of ReadDocument. */
  661.  
  662. #pragma segment TheDoc
  663. OSErr    WriteDocument(FileRecHndl frHndl)
  664. {
  665.     return(DefaultWriteDocument(frHndl));
  666. }
  667.  
  668.  
  669.  
  670. /*****************************************************************************/
  671.  
  672.  
  673.  
  674. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  675.  
  676. #pragma segment TheDoc
  677. OSErr    DoOpenApplication(void)
  678. {
  679.     return(noErr);
  680. }
  681.  
  682.  
  683.  
  684. /*****************************************************************************/
  685.  
  686.  
  687.  
  688. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  689.  
  690. #pragma segment TheDoc
  691. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  692. {
  693.     Boolean        redrawMenuBar;
  694.     MenuHandle    menu;
  695.  
  696.     redrawMenuBar = false;
  697.  
  698.     switch (menuID) {
  699.         case mFile:
  700.             redrawMenuBar = DoAdjustFileMenu(window);
  701.             break;
  702.         case mEdit:
  703.             redrawMenuBar = DoAdjustEditMenu(window);
  704.             break;
  705.         case mArrange:
  706.             redrawMenuBar = DoAdjustArrangeMenu(window);
  707.             break;
  708.         case mOther:
  709.             redrawMenuBar = DoAdjustOtherMenu(window);
  710.             break;
  711.         default:
  712.             if (menu = GetMHandle(menuID))
  713.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  714.             break;
  715.     }
  716.  
  717.     return(redrawMenuBar);
  718. }
  719.  
  720.  
  721.  
  722. /*****************************************************************************/
  723.  
  724.  
  725.  
  726. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  727.  
  728. #pragma segment TheDoc
  729. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  730. {
  731. #pragma unused (window)
  732.  
  733.     return(DoMenuCommand(menuID, menuItem));
  734. }
  735.  
  736.  
  737.  
  738. /*****************************************************************************/
  739. /*****************************************************************************/
  740. /*****************************************************************************/
  741.  
  742.  
  743.  
  744. #pragma segment DrawDoc
  745. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  746. {
  747.     OSErr        err;
  748.     TreeObjHndl    root;
  749.  
  750.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  751.         /* Create a document and root object to copy the file data into. */
  752.  
  753.     if (!err)
  754.         err = CopyChildren((*oldFrHndl)->d.doc.root, (**newFrHndl)->d.doc.root);
  755.             /* Copy the hierarchical data into the new file. */
  756.  
  757.     if (!err) {
  758.         root = (**newFrHndl)->d.doc.root;
  759.         DoFTreeMethod(root, UNDOMESSAGE, UNDOFROMDOC);
  760.         mDerefRoot(root)->numSelected = 0;
  761.     }        /* We have to be careful to use methods that don't try to draw
  762.             ** anything, as we don't have a window to draw into yet. */
  763.  
  764.     return(err);
  765. }
  766.  
  767.  
  768.  
  769. /*****************************************************************************/
  770.  
  771.  
  772.  
  773. /* This is called when a key event occurs and it is determined that it isn't
  774. ** a menu key. */
  775.  
  776. #pragma segment DrawDoc
  777. void    DoDelete(FileRecHndl frHndl)
  778. {
  779.     WindowPtr    window;
  780.     TreeObjHndl    root;
  781.     short        cnum;
  782.     Boolean        didDelete;
  783.  
  784.     root = (*frHndl)->d.doc.root;
  785.         /* All of the selected objects are children of the root, so all we have to do
  786.         ** is walk the children of the root looking for selected objects.  If the
  787.         ** document has grouping objects, which have sub-children, only the group
  788.         ** object is selected. */
  789.  
  790.     for (didDelete = false, cnum = (*root)->numChildren; cnum;) {
  791.         if (mDerefCommon(GetChildHndl(root, --cnum))->selected) {
  792.             if (!didDelete)
  793.                 NewDocumentUndo(frHndl);
  794.                     /* Make sure this delete event is posted in a separate undo from
  795.                     ** a possible previous delete event. */
  796.             DisposeChild(DELETE_EDIT, root, cnum);
  797.                 /* Post the delete of the child into the undo hierarchy. */
  798.             didDelete = true;
  799.                 /* Yes, we did change the document. */
  800.         }
  801.     }
  802.  
  803.     if (didDelete) {                            /* If something got deleted... */
  804.         window = (*frHndl)->fileState.window;
  805.         SetWindowDirty(window);                    /* Flag the document as dirty. */
  806.         BeginContent(window);                    /* Redraw the contents. */
  807.         DoImageDocument(frHndl);
  808.         EndContent(window);
  809.     }
  810. }
  811.  
  812.  
  813.  
  814. /*****************************************************************************/
  815.  
  816.  
  817.  
  818. #pragma segment DrawDoc
  819. void    DoArrange(FileRecHndl frHndl, short menuItem)
  820. {
  821.     TreeObjHndl    root;
  822.     short        cnum, cdir, cend;
  823.     WindowPtr    window;
  824.  
  825.     if (menuItem >= kGroup) {
  826.         switch (menuItem) {
  827.             case kGroup:
  828.                 DoGroup(frHndl);
  829.                 break;
  830.             case kUngroup:
  831.                 DoUngroup(frHndl);
  832.                 break;
  833.         }
  834.     }
  835.     else {
  836.         root = (*frHndl)->d.doc.root;
  837.  
  838.         cnum = 0;
  839.         cdir = 1;
  840.         cend = (*root)->numChildren;
  841.  
  842.         if (cend)
  843.             NewDocumentUndo(frHndl);
  844.  
  845.         if (menuItem >= kMoveBackward) {
  846.             cnum = cend - 1;
  847.             cdir = -1;
  848.             cend = -1;
  849.         }
  850.  
  851.         for (; cnum != cend; cnum += cdir) {
  852.             if (DoTreeObjMethod(GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  853.                 switch (menuItem) {
  854.                     case kMoveForward:
  855.                         if (cnum)
  856.                             MoveChild(MOVEFORWARD_EDIT, root, cnum, root, cnum - 1);
  857.                         break;
  858.                     case kMoveToFront:
  859.                         MoveChild(MOVETOFRONT_EDIT, root, cnum, root, 0);
  860.                         break;
  861.                     case kMoveBackward:
  862.                         MoveChild(MOVEBACKWARD_EDIT, root, cnum, root, cnum + 1);
  863.                         break;
  864.                     case kMoveToBack:
  865.                         MoveChild(MOVETOBACK_EDIT, root, cnum, root, -1);
  866.                         break;
  867.                 }
  868.             }
  869.         }
  870.     }
  871.     SetWindowDirty(window = (*frHndl)->fileState.window);
  872.  
  873.     BeginContent(window);
  874.     DoImageDocument(frHndl);
  875.     EndContent(window);
  876. }
  877.  
  878.  
  879.  
  880. /*****************************************************************************/
  881.  
  882.  
  883.  
  884. #pragma segment DrawDoc
  885. Rect    GetSelectedArea(TreeObjHndl root)
  886. {
  887.     TreeObjHndl    child;
  888.     short        cnum;
  889.     Rect        selectRct, rct;
  890.     Boolean        first;
  891.  
  892.     SetRect(&selectRct, 0, 0, 0, 0);
  893.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  894.         child = GetChildHndl(root, --cnum);
  895.         if (DoTreeObjMethod(child, GETSELECTMESSAGE, 0)) {
  896.             DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  897.             if (!EmptyRect(&rct)) {
  898.                 if (first) {
  899.                     selectRct = rct;
  900.                     first = false;
  901.                 }
  902.                 else UnionRect(&selectRct, &rct, &selectRct);
  903.             }
  904.         }
  905.     }
  906.     return(selectRct);
  907. }    
  908.  
  909.  
  910.  
  911. /*****************************************************************************/
  912.  
  913.  
  914.  
  915. #pragma segment DrawDoc
  916. Rect    GetDataArea(TreeObjHndl root)
  917. {
  918.     TreeObjHndl    child;
  919.     short        cnum;
  920.     Rect        areaRct, rct;
  921.     Boolean        first;
  922.  
  923.     SetRect(&areaRct, 0, 0, 0, 0);
  924.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  925.         child = GetChildHndl(root, --cnum);
  926.         DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  927.         if (!EmptyRect(&rct)) {
  928.             if (first) {
  929.                 areaRct = rct;
  930.                 first   = false;
  931.             }
  932.             else UnionRect(&areaRct, &rct, &areaRct);
  933.         }
  934.     }
  935.     return(areaRct);
  936. }    
  937.  
  938.  
  939.  
  940. /*****************************************************************************/
  941.  
  942.  
  943.  
  944. #pragma segment DrawDoc
  945. void    SetDataArea(TreeObjHndl root, short h, short v)
  946. {
  947.     if (h != kwNoChange) {
  948.         h /= (7 * 72);
  949.         ++h;
  950.         h *= (7 * 72);
  951.     }
  952.  
  953.     if (v != kwNoChange) {
  954.         v /= (10 * 72);
  955.         ++v;
  956.         v *= (10 * 72);
  957.     }
  958.  
  959.     SetDocSize(mDerefRoot(root)->frHndl, h, v);
  960. }    
  961.  
  962.  
  963.  
  964. /*****************************************************************************/
  965.  
  966.  
  967.  
  968. #pragma segment DrawDoc
  969. void    DoGroup(FileRecHndl frHndl)
  970. {
  971.     TreeObjHndl    root, group, child;
  972.     short        gnum, cnum;
  973.     Rect        groupRct;
  974.  
  975.     NewDocumentUndo(frHndl);
  976.  
  977.     root = (*frHndl)->d.doc.root;
  978.     for (gnum = 0; gnum < (*root)->numChildren; ++gnum)
  979.         if (DoTreeObjMethod(GetChildHndl(root, gnum), GETSELECTMESSAGE, 0)) break;
  980.  
  981.     if (!(group = NewChild(GROUP_EDIT, root, gnum, GROUPOBJ, 0))) return;
  982.  
  983.     groupRct = GetSelectedArea(root);
  984.     for (cnum = (*root)->numChildren - 1; cnum > gnum; --cnum) {
  985.         if (DoTreeObjMethod(child = GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  986.             DoTreeObjMethodClipped(child, SETSELECTMESSAGE, SELECTOFF);
  987.             MoveChild(GROUP_EDIT, root, cnum, group, 0);
  988.         }
  989.     }
  990.  
  991.     mDerefGroup(group)->group = groupRct;
  992. }    
  993.  
  994.  
  995.  
  996. /*****************************************************************************/
  997.  
  998.  
  999.  
  1000. #pragma segment DrawDoc
  1001. void    DoUngroup(FileRecHndl frHndl)
  1002. {
  1003.     TreeObjHndl    root, group;
  1004.     short        gnum, cnum;
  1005.  
  1006.     NewDocumentUndo(frHndl);
  1007.  
  1008.     root = (*frHndl)->d.doc.root;
  1009.     for (gnum = (*root)->numChildren; gnum;) {
  1010.         if (DoTreeObjMethod(group = GetChildHndl(root, --gnum), GETSELECTMESSAGE, 0)) {
  1011.             if ((*group)->type == GROUPOBJ) {
  1012.                 DoTreeSelect(group, SELECTOFF);
  1013.                 for (cnum = (*group)->numChildren; cnum;) {
  1014.                     MoveChild(UNGROUP_EDIT, group, --cnum, root, gnum + 1);
  1015.                     DoTreeSelect(GetChildHndl(root, gnum + 1), SELECTON);
  1016.                 }
  1017.                 DisposeChild(UNGROUP_EDIT, root, gnum);
  1018.             }
  1019.         }
  1020.     }
  1021. }    
  1022.  
  1023.  
  1024.  
  1025. /*****************************************************************************/
  1026. /*****************************************************************************/
  1027. /*****************************************************************************/
  1028.  
  1029.  
  1030.  
  1031. #pragma segment DrawDoc
  1032. void    AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click)
  1033. {
  1034.     TreeObjHndl        root, cobj, origHndl, oldHndl;
  1035.     WindowPtr        window;
  1036.     short            cnum, h, w, i;
  1037.     short            adding, extSelect;
  1038.     LayerObj        wlyr, wklyr, blyr;
  1039.     Rect            oldRct, newRct, rct, extRct1, extRct2, extRct3;
  1040.     OSErr            err;
  1041.     LayerDrawInfo    drawInfo;
  1042.     Boolean            tool, keepTool, newTool;
  1043.     Point            curMouse, org, oldOrg;
  1044.  
  1045.     root   = (*frHndl)->d.doc.root;
  1046.     window = (*frHndl)->fileState.window;
  1047.  
  1048.     tool      = GetTool();
  1049.     keepTool  = GetToolPersistence();
  1050.     adding    = false;
  1051.     extSelect = false;
  1052.     newTool   = false;
  1053.  
  1054.     if (!hndl) {        /* If hndl == nil, then we are adding a new object. */
  1055.         adding = true;
  1056.         if (!tool)
  1057.             extSelect = true;
  1058.         else
  1059.             DoTreeSelect(root, SELECTOFF);        /* Turn off all old selections. */
  1060.         click->offset.h = 0;
  1061.         click->offset.v = 0;
  1062.         click->grabber  = 0;
  1063.         click->oldFlip  = 0;
  1064.         click->newFlip  = 0;
  1065.  
  1066.         i = (extSelect) ? EXTSELECTOBJ : ((RECTOBJ - 1) + tool);
  1067.         hndl = NewChild(NO_EDIT, root, cnum = 0, i, 0);
  1068.             /* The child gets created selected, ready to go, except that it's rect is
  1069.             ** empty.  As the user drags the object out, this will change. */
  1070.  
  1071.         if (!keepTool) {
  1072.             tool    = 0;
  1073.             newTool = true;
  1074.         }            /* If the tool is a one-use tool, dispose it. */
  1075.  
  1076.         if (hndl) {        /* If we succeeded at creating something... */
  1077.             rct.top  = rct.bottom = click->localEvent.where.v;
  1078.             rct.left = rct.right  = click->localEvent.where.h;
  1079.             DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);
  1080.             DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1081.         }
  1082.     }
  1083.     else {
  1084.         NewDocumentUndo(frHndl);
  1085.         cnum = GetChildNum(hndl);
  1086.         if (ModifyChild(SIZE_EDIT, root, cnum, false)) return;
  1087.             /* Out of memory.  I would handle this case better if it weren't just sample code.
  1088.             ** Given that there are no complex objects, we actually can't run out of memory
  1089.             ** due to this operation.  However, there may someday be a pixmap type of object,
  1090.             ** whose memory hit could be substantial. */
  1091.     }
  1092.  
  1093.     drawInfo.root = root;        /* The tree we are operating with. */
  1094.     drawInfo.cnum = cnum;        /* The backmost object that is changing. */
  1095.  
  1096.     if (hndl) {
  1097.         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1098.         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1099.         if (!err)
  1100.             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1101.         if (err) DisposeThisAndBelowLayers(wklyr);
  1102.             /* The above code creates the necessary offscreen layers for the following editing.
  1103.             ** The window layer is bound to succeed at getting created, as it uses the window,
  1104.             ** instead of creating an offscreen GWorld.  The work layer and back layer creations
  1105.             ** may fail.  If they do, then we will only have a window layer.  The window layer
  1106.             ** is smart enough to notice that it is alone, and if it is, then it will do the
  1107.             ** drawing directly to the window.  Drawing directly to the window means that there
  1108.             ** will be a bunch of flicker, but what else is there to do when there is not enough
  1109.             ** ram?  Drawing nothing is even worse. */
  1110.  
  1111.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1112.             /* Bounding rect of the object getting created or resized. */
  1113.  
  1114.         if (click->grabber)
  1115.             InsetRect(&rct, -3, -3);
  1116.         InvalLayer(wlyr, rct, true);
  1117.             /* If getting resized, then the object has grabbers, and we have to make sure
  1118.             ** that the area getting redrawn is large enough for the grabbers. */
  1119.  
  1120.         origHndl = hndl;
  1121.         HandToHand((Handle *)&origHndl);
  1122.         while (StillDown()) {
  1123.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  1124.             oldHndl = hndl;
  1125.             HandToHand((Handle *)&oldHndl);
  1126.             if (DoTreeObjMethod(hndl, SIZEMESSAGE, (long)click)) {        /* If new size... */
  1127.                 DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&newRct);
  1128.                 if (!err) {        /* If we have all offscreen layers... */
  1129.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1130.                     if (click->grabber)
  1131.                         InsetRect(&rct, -3, -3);
  1132.                     InvalLayer(wlyr, rct, true);
  1133.                     UpdateLayer(wlyr);
  1134.                 }
  1135.                 else {            /* If we only have wlyr... */
  1136.                     SwapTreeObjData(hndl, oldHndl);
  1137.                     if (!EqualTreeObjData(hndl, origHndl))
  1138.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1139.                     SwapTreeObjData(hndl, oldHndl);
  1140.                     if (!EqualTreeObjData(hndl, origHndl))
  1141.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1142.                 }
  1143.             }
  1144.             DisposeHandle((Handle)oldHndl);
  1145.  
  1146.             GetMouse(&curMouse);
  1147.             GetContentRect(window, &rct);
  1148.             if (!PtInRect(curMouse, &rct)) {
  1149.  
  1150.                 GetContentOrigin(window, &org);
  1151.                 oldOrg = org;
  1152.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1153.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1154.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1155.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1156.  
  1157.                 rct = GetDataArea(root);
  1158.                 if (rct.right < curMouse.h)
  1159.                     rct.right = curMouse.h;
  1160.                 if (rct.bottom < curMouse.v)
  1161.                     rct.bottom = curMouse.v;
  1162.                 SetDataArea(root, rct.right, rct.bottom);
  1163.  
  1164.                 SetContentOrigin(window, org.h, org.v);
  1165.                 GetContentOrigin(window, &org);
  1166.  
  1167.                 if (!err) {
  1168.                     if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1169.                         DisposeThisAndBelowLayers(wlyr);
  1170.                         DoImageDocument(frHndl);
  1171.                         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1172.                         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1173.                         if (!err)
  1174.                             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1175.                         if (err) DisposeThisAndBelowLayers(wklyr);
  1176.                         GetContentRect(window, &rct);
  1177.                         InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1178.                     }
  1179.                 }
  1180.                 else
  1181.                     DoImageDocument(frHndl);
  1182.             }
  1183.             else {
  1184.                 rct = GetDataArea(root);
  1185.                 SetDataArea(root, rct.right, rct.bottom);
  1186.             }
  1187.         }
  1188.         if (origHndl)
  1189.             DisposeHandle((Handle)origHndl);
  1190.  
  1191.         rct = GetDataArea(root);
  1192.         SetDataArea(root, rct.right, rct.bottom);
  1193.  
  1194.         DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&rct);
  1195.         h = rct.bottom - rct.top;
  1196.         w = rct.right  - rct.left;
  1197.             /* This is how big the final product is.  It may now be unacceptably small.
  1198.             ** If we were adding a new object and it is too small, get rid of it entirely
  1199.             ** and use this to deselect the tool.  If we were resizing an existing object,
  1200.             ** then force the object to be a minimum size. */
  1201.  
  1202.         if (((!h) || (!w)) || ((h < 8) && (w < 8))) {    /* If smaller than minimum... */
  1203.             if (adding) {
  1204.                 DisposeChild(NO_EDIT, root, 0);        /* Don't create a new object that's too small. */
  1205.                 hndl = nil;                    /* We didn't change anything, so flag this situation.  */
  1206.                 SetPaletteTool(0);
  1207.                 newTool = false;
  1208.             }
  1209.             else {
  1210.                 rct.bottom = rct.top  + 8;                /* Don't let them shrink it too small. */
  1211.                 rct.right  = rct.left + 8;
  1212.                 DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);    /* Make it minimum. */
  1213.             }
  1214.             DoImageDocument(frHndl);
  1215.         }
  1216.  
  1217.         if (hndl) {        /* If we added or changed something... */
  1218.  
  1219.  
  1220.             if (adding) {        /* If we are adding, we haven't handled posting the addition
  1221.                                 ** for undo.  The reason for this is that the user may not
  1222.                                 ** actually add an object.  If they just click and release,
  1223.                                 ** there is no object added.  If we add the child with undo
  1224.                                 ** posting, and then remove it if the user doesn't grow it
  1225.                                 ** out, then we may lose some old undo info.  If the user
  1226.                                 ** doesn't grow out the object, the operation is a NOP, and
  1227.                                 ** so old undo information shouldn't be lost. */
  1228.                 if (extSelect) {                            /* We only created the object for extend-select purposes. */
  1229.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&extRct1);
  1230.                     for (i = (*root)->numChildren - 1; i; --i) {
  1231.                         cobj = GetChildHndl(root, i);
  1232.                         DoTreeObjMethod(cobj, GETBBOXMESSAGE, (long)&extRct2);
  1233.                         SectRect(&extRct1, &extRct2, &extRct3);
  1234.                         if (EqualRect(&extRct2, &extRct3))
  1235.                             DoTreeObjMethodClipped(cobj, SETSELECTMESSAGE, SELECTTOGGLE);
  1236.                     }
  1237.                     DisposeChild(NO_EDIT, root, 0);            /* We're done with it, so kill it. */
  1238.                 }
  1239.                 else {
  1240.                     NewDocumentUndo(frHndl);
  1241.                     hndl = CopyChild(NEW_EDIT, root, 0, root, 0, false);
  1242.                     DisposeChild(NO_EDIT, root, 1);
  1243.                 }    /* We just created a posted copy of the child, just as if we had called
  1244.                     ** NewChild.  We also disposed of the temporary unposted copy of the child.
  1245.                     ** The only problem is that objects entering the document get flagged as
  1246.                     ** selected.  This means that when we select the object, nothing happens
  1247.                     ** on the screen because the object thinks it is already selected. */
  1248.                 if (!extSelect)
  1249.                     if (hndl)
  1250.                         DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1251.                             /* Now the select grabbers can be drawn. */
  1252.             }
  1253.  
  1254.             rct = GetDataArea(root);
  1255.             SetDataArea(root, rct.right, rct.bottom);
  1256.  
  1257.             SetWindowDirty(window);
  1258.             if (err)
  1259.                 DoImageDocument(frHndl);
  1260.                     /* If we have only a wlyr, then we haven't really been generating a
  1261.                     ** complete image.  Redraw the document so that a full image is shown. */
  1262.  
  1263.             if (!extSelect)
  1264.                 DoTreeObjMethodClipped(hndl, SETSELECTMESSAGE, SELECTON);
  1265.                     /* Make sure the object is selected. */
  1266.         }
  1267.  
  1268.         DisposeThisAndBelowLayers(wlyr);        /* Clean up. */
  1269.     }
  1270.  
  1271.     if (extSelect)
  1272.         DoImageDocument(frHndl);
  1273.             /* Since we have killed the extend-select object, erase it from the screen. */
  1274.  
  1275.     if (newTool)
  1276.         SetPaletteTool(tool);
  1277. }
  1278.  
  1279.  
  1280.  
  1281. /*****************************************************************************/
  1282.  
  1283.  
  1284.  
  1285. #pragma segment DrawDoc
  1286. static OSErr    WindowLayerProc(LayerObj theLayer, short message)
  1287. {
  1288.     OSErr            err;
  1289.     WindowPtr        oldPort, window;
  1290.     Point            contOrg;
  1291.     Rect            contRct, thisUpdate;
  1292.     LayerDrawInfo    drawInfo;
  1293.     RgnHandle        oldClip, newClip;
  1294.  
  1295.     err = noErr;
  1296.  
  1297.     drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1298.  
  1299.     switch (message) {
  1300.         case kLayerInit:
  1301.             err = DefaultLayerProc(theLayer, message);
  1302.             GetPort(&oldPort);
  1303.             SetPort(window = (*theLayer)->layerPort);
  1304.             GetContentOrigin(window, &contOrg);
  1305.             SetOrigin(contOrg.h, contOrg.v);
  1306.             GetContentRect(window, &contRct);
  1307.             (*theLayer)->dstRect = contRct;
  1308.                 /* The above calculates the content rect (less tool palette and scrollbars).
  1309.                 ** By setting the dstRect to this smaller rect, work layer and back layer
  1310.                 ** will be created the size of dstRect, instead of the portRect of the
  1311.                 ** window.  This in turn will keep the GWLayers code from drawing over
  1312.                 ** the tool palette and scrollbars. */
  1313.             SetPort(oldPort);
  1314.             break;
  1315.         case kLayerUpdate:
  1316.             BeginContent((*theLayer)->layerPort);
  1317.             if ((*theLayer)->belowLayer)
  1318.                 DefaultLayerProc(theLayer, message);
  1319.             else {        /* If offscreen layers couldn't be made, then do it by hand. */
  1320.                 thisUpdate = UpdateUpdateRects(theLayer);
  1321.                 oldClip = NewRgn();
  1322.                 newClip = NewRgn();
  1323.                 RectRgn(newClip, &thisUpdate);
  1324.                 GetClip(oldClip);
  1325.                 SetClip(newClip);
  1326.                 EraseRect(&thisUpdate);
  1327.                 DoTreeDraw(drawInfo.root, DRAWOBJ);
  1328.                 DoTreeDraw(drawInfo.root, DRAWSELECT);
  1329.                 SetClip(oldClip);
  1330.                 DisposeRgn(oldClip);
  1331.                 DisposeRgn(newClip);
  1332.                 DrawPageGrid();
  1333.             }
  1334.             EndContent((*theLayer)->layerPort);
  1335.             break;
  1336.  
  1337.         default:
  1338.             err = DefaultLayerProc(theLayer, message);
  1339.                 /* Default behavior for everything else. */
  1340.             break;
  1341.     }
  1342.  
  1343.     return(err);
  1344. }
  1345.  
  1346.  
  1347.  
  1348. /*****************************************************************************/
  1349.  
  1350.  
  1351.  
  1352. #pragma segment DrawDoc
  1353. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1354. {
  1355.     OSErr            err;
  1356.     Rect            rct;
  1357.     LayerDrawInfo    drawInfo;
  1358.     short            cnum;
  1359.     TreeObjHndl        chndl;
  1360.  
  1361.     err = noErr;
  1362.  
  1363.     switch (message) {
  1364.         case kLayerInit:
  1365.             err = DefaultLayerProc(theLayer, message);
  1366.             break;
  1367.         case kLayerUpdate:
  1368.             SetLayerWorld(theLayer);
  1369.             drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1370.             if ((*theLayer)->belowLayer)
  1371.                 DefaultLayerProc(theLayer, message);
  1372.             else {
  1373.                 rct = GetEffectiveDstRect(theLayer);
  1374.                 EraseRect(&rct);
  1375.                 drawInfo.cnum = (*drawInfo.root)->numChildren - 1;
  1376.             }
  1377.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1378.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1379.                 DoTreeDraw(chndl, DRAWOBJ);
  1380.             }
  1381.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1382.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1383.                 DoTreeDraw(chndl, DRAWSELECT);
  1384.             }
  1385.             DrawPageGrid();
  1386.             ResetLayerWorld(theLayer);
  1387.             break;
  1388.         default:
  1389.             err = DefaultLayerProc(theLayer, message);
  1390.                 /* Default behavior for everything else. */
  1391.             break;
  1392.     }
  1393.  
  1394.     return(err);
  1395. }
  1396.  
  1397.  
  1398.  
  1399. /*****************************************************************************/
  1400.  
  1401.  
  1402.  
  1403. #pragma segment DrawDoc
  1404. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1405. {
  1406.     OSErr            err;
  1407.     Rect            rct;
  1408.     LayerDrawInfo    drawInfo;
  1409.     short            cnum;
  1410.     TreeObjHndl        chndl;
  1411.  
  1412.     err = noErr;
  1413.  
  1414.     switch (message) {
  1415.         case kLayerInit:
  1416.             err = DefaultLayerProc(theLayer, message);
  1417.             if (!err) {
  1418.                 SetLayerWorld(theLayer);
  1419.                 rct = GetEffectiveDstRect(theLayer);
  1420.                 EraseRect(&rct);
  1421.                 drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1422.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1423.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1424.                     DoTreeDraw(chndl, DRAWOBJ);
  1425.                 }
  1426.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1427.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1428.                     DoTreeDraw(chndl, DRAWSELECT);
  1429.                 }
  1430.                 ResetLayerWorld(theLayer);
  1431.             }
  1432.             break;
  1433.         default:
  1434.             err = DefaultLayerProc(theLayer, message);
  1435.                 /* Default behavior for everything else. */
  1436.             break;
  1437.     }
  1438.  
  1439.     return(err);
  1440. }
  1441.  
  1442.  
  1443.  
  1444. /*****************************************************************************/
  1445.  
  1446.  
  1447.  
  1448. #pragma segment DrawDoc
  1449. void    DrawPageGrid(void)
  1450. {
  1451.     WindowPtr    curPort;
  1452.     short        x1, x2, y1, y2, i;
  1453.  
  1454.     if (!gPrintPage) {
  1455.         GetPort(&curPort);
  1456.         PenPat((ConstPatternParam)&qd.gray);
  1457.         x1  = curPort->portRect.left   / (7 * 72);
  1458.         x1 *= (7 * 72);
  1459.         x2  = curPort->portRect.right  / (7 * 72);
  1460.         x2 *= (7 * 72);
  1461.         y1  = curPort->portRect.top    / (10 * 72);
  1462.         y1 *= (10 * 72);
  1463.         y2  = curPort->portRect.bottom / (10 * 72);
  1464.         y2 *= (10 * 72);
  1465.         for (i = x1; i <= x2; i += (7 * 72)) {
  1466.             MoveTo(i - 1, -1);
  1467.             Line  (0, 16383);
  1468.         }
  1469.         for (i = y1; i <= y2; i += (10 * 72)) {
  1470.             MoveTo(-1, i - 1);
  1471.             Line  (16383, 0);
  1472.         }
  1473.         PenNormal();
  1474.     }
  1475. }
  1476.  
  1477.  
  1478.  
  1479. /*****************************************************************************/
  1480.  
  1481.  
  1482.  
  1483. #pragma segment DrawDoc
  1484. void    SlideSelection(FileRecHndl frHndl, ClickInfo *click)
  1485. {
  1486.     LayerDrawInfo    drawInfo;
  1487.     WindowPtr        window;
  1488.     LayerObj        wlyr, wklyr, blyr;
  1489.     Point            lastMouse, curMouse;
  1490.     short            cnum;
  1491.     TreeObjHndl        root, chndl;
  1492.     Rect            newLoc, selectRct, rct;
  1493.     Point            oldOrg, org;
  1494.     Boolean            changed;
  1495.     OSErr            err;
  1496.  
  1497.     changed = false;
  1498.     NewDocumentUndo(frHndl);
  1499.  
  1500.     root = (*frHndl)->d.doc.root;
  1501.     for (cnum = (*root)->numChildren;;) {
  1502.         if (DoTreeObjMethod(GetChildHndl(root, --cnum), GETSELECTMESSAGE, 0)) break;
  1503.         if (!cnum) return;
  1504.     }
  1505.  
  1506.     drawInfo.root = root;
  1507.     drawInfo.cnum = cnum;
  1508.     window        = (*frHndl)->fileState.window;
  1509.  
  1510.                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1511.               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1512.     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1513.  
  1514.     selectRct = GetSelectedArea(root);
  1515.     InsetRect(&selectRct, -3, -3);
  1516.     InvalLayer(wlyr, selectRct, false);
  1517.     lastMouse = click->localEvent.where;
  1518.     while (StillDown()) {
  1519.         GetMouse(&curMouse);
  1520.         click->offset.h = (curMouse.h - lastMouse.h);
  1521.         click->offset.v = (curMouse.v - lastMouse.v);
  1522.         click->message  = CLICKDRAG;
  1523.         if ((click->offset.h) || (click->offset.v)) {
  1524.             changed = true;
  1525.             for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  1526.                 chndl = GetChildHndl(root, cnum);
  1527.                 if (mDerefCommon(chndl)->selected) {
  1528.                     ModifyChild(MOVE_EDIT, root, cnum, true);
  1529.                     DoFTreeMethod(chndl, CLICKMESSAGE, (long)click);
  1530.                     DoTreeObjMethod(chndl, GETBBOXMESSAGE, (long)&newLoc);
  1531.                     InsetRect(&newLoc, -3, -3);
  1532.                     InvalLayer(wlyr, newLoc, true);
  1533.                 }
  1534.             }
  1535.             UpdateLayer(wlyr);
  1536.             lastMouse = curMouse;
  1537.  
  1538.             GetContentRect(window, &rct);
  1539.             if (!PtInRect(curMouse, &rct)) {
  1540.  
  1541.                 GetContentOrigin(window, &org);
  1542.                 oldOrg = org;
  1543.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1544.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1545.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1546.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1547.  
  1548.                 rct = GetDataArea(root);
  1549.                 if (rct.right < curMouse.h)
  1550.                     rct.right = curMouse.h;
  1551.                 if (rct.bottom < curMouse.v)
  1552.                     rct.bottom = curMouse.v;
  1553.                 SetDataArea(root, rct.right, rct.bottom);
  1554.  
  1555.                 SetContentOrigin(window, org.h, org.v);
  1556.                 GetContentOrigin(window, &org);
  1557.                 if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1558.                     DisposeThisAndBelowLayers(wlyr);
  1559.                     DoImageDocument(frHndl);
  1560.                                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1561.                               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1562.                     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1563.                     if (err) DisposeThisAndBelowLayers(wklyr);
  1564.                     GetContentRect(window, &rct);
  1565.                     InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1566.                 }
  1567.             }
  1568.             else {
  1569.                 rct = GetDataArea(root);
  1570.                 SetDataArea(root, rct.right, rct.bottom);
  1571.             }
  1572.         }
  1573.     }
  1574.  
  1575.     rct = GetDataArea(root);
  1576.     SetDataArea(root, rct.right, rct.bottom);
  1577.  
  1578.     if (changed)
  1579.         SetWindowDirty(window);
  1580.  
  1581.     DisposeThisAndBelowLayers(wlyr);
  1582. }
  1583.  
  1584.  
  1585.  
  1586. /*****************************************************************************/
  1587.  
  1588.  
  1589.  
  1590. #pragma segment DrawDoc
  1591. void    NewDocumentUndo(FileRecHndl frHndl)
  1592. {
  1593.     NewUndo((*frHndl)->d.doc.root);
  1594. }
  1595.  
  1596.  
  1597.  
  1598.